<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3.后期处理通道增强图像效果-烟雾效果</title>
    <style>
        canvas{
            border:1px solid #aaa;
            margin: 0 auto;
            display: block;
        }
    </style>
</head>
<body>
    <canvas width="554" height="554"></canvas>
    <script src="/common/lib/gl-renderer.js"></script>
    <script type="module">
        import { loadImage } from "/common/lib/utils.js";

        (async function(){
            /**
             * gl-renderer 在 WebGL 底层的基础上进行了一些简单的封装，以便于我们将重点放在提供几何数据、设置变量和编写 Shader 上，不用因为创建 buffer 等细节而分心
             * 第一步: 创建 Renderer 对象
             * 第二步: 创建并启用 WebGL 程序
             * 第三步: 设置 uniform 变量
             * 第四步: 将顶点数据送入缓冲区
             * 第五步: 渲染
            */
            const canvas = document.querySelector('canvas'); 
            const renderer = new GlRenderer(canvas);

            // 第二步

            const vertex = `
                attribute vec2 a_vertexPosition;
                attribute vec2 uv;
                varying vec2 vUv;
                
                void main() {
                    gl_PointSize = 1.0;
                    vUv = uv;
                    gl_Position = vec4(a_vertexPosition, 1, 1);
                }
            `;
            // 圆
            const fragment = `
                #ifdef GL_ES
                precision highp float;
                #endif

                varying vec2 vUv;
                uniform sampler2D tMap;
                uniform float uTime;

                float random (vec2 st) {
                    return fract(sin(dot(st.xy,
                                        vec2(12.9898,78.233)))*
                        43758.5453123);
                }

                float noise(vec2 st) {
                    vec2 i = floor(st);
                    vec2 f = fract(st);
                    vec2 u = f * f * (3.0 - 2.0 * f);
                    // st与方形区域的四个顶点做插值计算
                    return mix( mix( random( i + vec2(0.0,0.0) ), random( i + vec2(1.0,0.0) ), u.x),
                                mix( random( i + vec2(0.0,1.0) ), random( i + vec2(1.0,1.0) ), u.x), u.y);
                }
                
                void main() {
                    vec3 smoke = vec3(0);
                    // 初次才会执行uTime<=0的代码
                    if(uTime <= 0.0) {
                        vec2 st = vUv - vec2(0.5);
                        float d = length(st);
                        // smoke = vec3(1.0 - smoothstep(0.05, 0.055, d));
                        smoke = vec3(step(d, 0.05));
                    }
                    vec2 st = vUv;

                    float offset = 1.0 / 554.0;
                    vec3 diffuse = texture2D(tMap, st).rgb;

                    // 烟雾形成效果处理
                    vec4 left = texture2D(tMap, st + vec2(-offset, 0.0));
                    vec4 right = texture2D(tMap, st + vec2(offset, 0.0));
                    vec4 up = texture2D(tMap, st + vec2(0.0, -offset));
                    vec4 down = texture2D(tMap, st + vec2(0.0, offset));

                    //加入噪声处理
                    float rand = noise(st + 5.0 * uTime);
                    float diff = 8.0 * 0.016 * ( (1.0 + rand) * left.r + (1.0 - rand) * right.r + down.r + 2.0 * up.r - 5.0 * diffuse.r );

                    gl_FragColor.rgb = (diffuse + diff) + smoke;
                    gl_FragColor.a = 1.0;
                }
            `

            const program = renderer.compileSync(fragment,vertex);
            renderer.useProgram(program);
    
            // 第三步

            // 第四步
            renderer.setMeshData([
                {
                    positions:[
                        [-1, -1], 
                        [-1, 1], 
                        [1, 1], 
                        [1, -1]
                    ],
                    attributes:{
                        uv:[
                            [0, 0], 
                            [0, 1], 
                            [1, 1], 
                            [1, 0],
                        ]
                    },
                    cells:[[0,1,2],[2,0,3]]
                }
            ])

            // 第五步
            // renderer.render()
            
            const fbo = {
                readFBO: renderer.createFBO(),
                writeFBO: renderer.createFBO(),
                get texture() {
                    return this.readFBO.texture;
                },
                swap() {
                    const tmp = this.writeFBO;
                    this.writeFBO = this.readFBO;
                    this.readFBO = tmp;
                },
            };

            function update(t) {
                // 输出到画布
                renderer.bindFBO(null);
                renderer.uniforms.uTime = t / 1000;
                renderer.uniforms.tMap = fbo.texture;
                renderer.render();
                // 同时输出到FBO
                renderer.bindFBO(fbo.writeFBO);
                renderer.uniforms.tMap = fbo.texture;
                // 交换读写缓冲以便下一次写入
                fbo.swap();
                renderer.render();
                requestAnimationFrame(update);
            }
            update(0);

        })()
        
    </script>
</body>
</html>